<?php

class Crud_Db_Table
{
	/**
	 * Zend_Db_Table object.
	 * 
	 * @var Zend_Db_Table $table
	 */
	protected $_table;
	
	/**
	 * Columns array. Key is column name. And value is columnClass object.
	 * 
	 * @var array
	 */
	protected $_columns = null;
	
	/**
	 * @var AdminSectionRow
	 */
	protected $_section;
	
	/**
	 * Create wrapped Db table.
	 * 
	 * @param mixed $table
	 * @return void
	 */
	public function __construct($table)
	{
		$table = $this->_getTable($table);
		$this->setTable($table);
	}
	
	/**
	 * Call wrapped row methods.
	 * 
	 * @param string $method
	 * @param array $args
	 * @return mixed
	 */
	public function __call($method, $args)
	{
		return call_user_func_array(array($this->getTable(), $method), $args);
	}
	
	/**
	 * Return wrapped Zend_Db_Table.
	 * 
	 * @return Zend_Db_Table
	 */
	public function getTable()
	{
		return $this->_table;
	}
	
	/**
	 * Set wrapped table.
	 * 
	 * @param Zend_Db_Table $table
	 * @return void
	 */
	public function setTable(Zend_Db_Table_Abstract $table)
	{
		$this->_table = $table;
	}
	
	/**
     * Fetches a new blank row (not from the database).
     * 
     * @return Crud_Db_Table_Row
     */
	public function createRow()
	{
		$row = $this->getTable()->createRow();
		$row = new Crud_Db_Table_Row($this, $row);
		return $row;
	}
	
	/**
	 * Return db table name.
	 * 
	 * @return string
	 */
	public function getTableName()
	{
		return $this->getTable()->info(Zend_Db_Table_Abstract::NAME);
	}

	/**
	 * Get table metadata.
	 * 
	 * @return array
	 */
	public function getMetadata()
	{
		$metadata = $this->getTable()->info(Zend_Db_Table::METADATA);
		return $metadata;
	}
	
	/**
	 * Return dependent many2many tables list.
	 * 
	 * @return array
	 */
	public function getMany2ManyTables()
	{
		$dependentTables = $this->getDependentTables();
		
		$many2manyTables = array();
		foreach ($dependentTables as $name => $table) {
			if ($table->isMany2Many()) {
				$many2manyTables[$name] = $table;
			}
		}
		
		return $many2manyTables;
	}
	
	/**
	 * Return dependent NOT many2many tables list.
	 * 
	 * @return array
	 */
	public function getNotMany2ManyTables()
	{
		$dependentTables = $this->getDependentTables();
		
		$notMany2manyTables = array();
		foreach ($dependentTables as $name => $table) {
			if (!$table->isMany2Many()) {
				$notMany2manyTables[$name] = $table;
			}
		}
		
		return $notMany2manyTables;
	}
	
	/**
	 * Return dependent tables list.
	 * 
	 * @return array
	 */
	public function getDependentTables()
	{
		$dependentTables = (array)$this->getTable()->info(Zend_Db_Table::DEPENDENT_TABLES);
		
		$class = get_class($this);
		$return = array();
		foreach ($dependentTables as $table) {
			$object = new $class($table);
			$name = $object->getName();
			$return[$name] = $object;
		}
		
		return $return;
	}
	
	/**
	 * Return true if table is many2many.
	 * 
	 * @return boolean
	 */
	public function isMany2Many()
	{
		$name = $this->getTableName();
		if ((false !== strpos($name, '2')) && substr($name, -1) != '2') {
			return true;
		}
		
		return false;
	}
	
	/**
	 * Return array of table columns.
	 * Key is column db name. Value if Crud_Db_Table_Column object.
	 * 
	 * @return array
	 */
	public function getColumns()
	{
		if ($this->_columns !== null) {
			return $this->_columns;
		}
		
		$columns = $this->getTable()->info(Zend_Db_Table_Abstract::COLS);
		
		$this->_columns = array();
		foreach ($columns as $column) {
			$object = Crud_Db_Table_Column::factory($this, $column);
			$this->_columns[$column] = $object;
		}

		// Order for columns
		$section = $this->getSection();
		if ($section) {
			$select = AdminSectionPropertyPeer::getInstance()->select();
			$select->order('order asc');
			$rowset = $section->getAdminSectionPropertyRowsetBySectionId($select);
	
			$nColumns = array();
			foreach ($rowset as $row) {
				$name = $row->getKey();
				if (isset($this->_columns[$name])) {
					$nColumns[$name] = $this->_columns[$name];
				}
			}
			
			$this->_columns = array_merge($nColumns, $this->_columns);
		}
		
		return $this->_columns;
	}

	/**
	 * Return true when table has column with given name.
	 * 
	 * @param string $column
	 * @return boolean
	 */
	public function hasColumn($column)
	{
		try {
			$this->getColumn($column);
		} catch (Crud_Exception $e) {
			return false;
		}
		
		return true;
	}
	
	/**
	 * Return single column object.
	 * 
	 * @param string $key
	 * @return Crud_Db_Table_Column
	 */
	public function getColumn($key)
	{
		$columns = $this->getColumns();
		if (!isset($columns[$key])) {
			throw new Crud_Exception("Column `$key` is not exists");
		}
		
		return $columns[$key];
	}
	
	/**
	 * Isset for column.
	 * 
	 * @param string $key
	 * @return boolean
	 */
	public function issetColumn($key)
	{
		$columns = $this->getColumns();
		return isset($columns[$key]);
	}
	
	/**
	 * Return column for show in other tables as foreign key information.
	 * Try get it from config and use 'name' or 'key' by default.
	 * By default column if ID.
	 * 
	 * @return string
	 */
	public function getPrimaryFKColumn()
	{
		$section = $this->getSection();
		$column = null;
		if ($section) {
			$column = $section->getMainProperty();
		}
		
		if (!$column) {
			$columns = array_keys($this->getColumns());
			if (in_array('name', $columns)) {
				$column = 'name';
			} else if (in_array('key', $columns)) {
				$column = 'key';
			} else {
				$column = 'id';
			}
		}
		
		return $column;
	}
	
	/**
	 * Return Admin section row.
	 * 
	 * @return AdminSectionRow
	 */
	public function getSection()
	{
		if (!$this->_section instanceof AdminSectionRow) {
			$name = $this->getTableName();
			$this->_section = AdminSectionPeer::getInstance()->fetchBy('table_name', $name);
		}
		
		return $this->_section;
	}
	
	public function setSection(AdminSectionRow $row)
	{
		$this->_section = $row;
	}
	
	/**
	 * Return column with foreign key for given table.
	 * 
	 * @param Crud_Db_Table
	 * @return Crud_Db_Table_Column
	 */
	public function getFKColumnFor($table)
	{
		$columns = $this->getColumns();
		foreach ($columns as $column) {
			if (!$column->isFK()) {
				continue;
			}
			
			if ($column->getFKTable()->getTableName() == $table->getTableName()) {
				return $column;
			}
		}
		
		return null;
	}
	
	/**
	 * Return first column with have not foreign key for given table.
	 * 
	 * @param Crud_Db_Table
	 * @return Crud_Db_Table_Column
	 */
	public function getFKColumnNotFor($table)
	{
		$columns = $this->getColumns();
		foreach ($columns as $column) {
			if (!$column->isFK()) {
				continue;
			}
			
			if ($column->getFKTable()->getTableName() != $table->getTableName()) {
				return $column;
			}
		}
		
		return null;
	}
	
	/**
	 * Return table configuration.
	 * 
	 * @return Zend_Config
	 */
	public function getConfig()
	{	
		$name = $this->_table->info(Zend_Db_Table_Abstract::NAME);
		$config = Crud_Config::getInstance()->$name;
		return $config;
	}
	
	/**
	 * Return table class by table name.
	 * 
	 * @param mixed $table
	 * @return Zend_Db_Table_Abstract
	 */
	protected function _getTable($table)
	{
		if (substr($table, -4) == 'Peer') {
			$peerName = $table;
		} else {
			$filter = new Zend_Filter_Word_UnderscoreToCamelCase();
			$peerName = $filter->filter($table);
			$peerName .= 'Peer';
		}
		
		$callback = array($peerName, 'getInstance');
		if (!is_callable($callback)) {
			throw new Crud_Exception("Cant create wrapper by given table name: `$table`");
		}
		
		$peer = call_user_func($callback);
		return $peer;
	}
}